/****************************************************************************
 * Copyright 2010 Huascar A. Sanchez                                        *
 *                                                                          *
 * Licensed under the Apache License, Version 2.0 (the "License");          *
 * you may not use this file except in compliance with the License.         *
 * You may obtain a copy of the License at                                  *
 *                                                                          *
 *     http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                          *
 * Unless required by applicable law or agreed to in writing, software      *
 * distributed under the License is distributed on an "AS IS" BASIS,        *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 * See the License for the specific language governing permissions and      *
 * limitations under the License.                                           *
 ****************************************************************************/
package com.gotobject.greatq;

import com.gotobject.greatq.datastore.DatastoreDriver;

import java.sql.Connection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;

import static java.util.Arrays.asList;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class GreatQ {
    private static final AtomicReference<ConnectionInfo> CONNECTION_INFO = new AtomicReference<ConnectionInfo>();
    private static final String PROTOCOL_CLASS = "com.gotobject.greatq.datastore.DatastoreDriver";
    public static final AtomicReference<ConnectionFactory> CONNECTION_FACTORY = new AtomicReference<ConnectionFactory>();
    private GreatQ(){}

    public static void assignConnectionFactory(ConnectionFactory factory){
        CONNECTION_FACTORY.set(factory);
    }

    public static void connect(){
        connect("greatq:https://jiqladmin.appspot.com/jiqlservlet?", "admin", "jiql");
    }

    public static void connect(String url, String username, String password){
        CONNECTION_INFO.compareAndSet(null, new ConnectionInfo(url, username, password));
    }

    public static QueryService createQueryService(Module... modules){
        final boolean isConnected = CONNECTION_INFO.get() != null;        
        return createQueryService(isConnected ? CONNECTION_INFO.get() : connectAndGet(), asList(modules));
    }

    private static ConnectionInfo connectAndGet(){
        connect();
        return CONNECTION_INFO.get();
    }

    private static boolean isConnectionFactoryNull(){
        return CONNECTION_FACTORY.get() == null;
    }

    private static QueryService createQueryService(ConnectionInfo info, List<Module> modules){
        if(info == null) throw new Error();
        // if connection is null, then create a brand new one...however, if there is one, dont create anything.
        final QuerySubmitterImpl querier = new QuerySubmitterImpl(
                isConnectionFactoryNull()
                        ? createConnection(info.getUrl(), info.getProps())
                        : CONNECTION_FACTORY.get().createConnection(info.getUrl(), info.getProps())
        );
        for(Module each : modules) {
            querier.install(each);
        }
        return querier.createQueryService();
    }

    private static Connection createConnection(String url, Properties props){
        CONNECTION_FACTORY.set(new DefaultConnectionFactory());
        return CONNECTION_FACTORY.get().createConnection(url, props);
    }

    private static class ConnectionInfo {
        private final String url;
        private final Properties props;
        ConnectionInfo(String url, String username, String password) {
            props = new Properties();
            this.url = url;
            props.put("user",username);
            props.put("password",password);
        }

        String getUrl(){
            return url;
        }

        Properties getProps(){
            return new Properties(props);
        }
    }


    private static class DefaultConnectionFactory implements ConnectionFactory {
        @Override
        public Connection createConnection(String url, Properties props) {
        final Connection connection;
        try {
            final Class<?> clazz = Class.forName(PROTOCOL_CLASS);
            final DatastoreDriver driver = (DatastoreDriver) clazz.newInstance();
            connection = driver.connect(url,props);
        } catch (Exception e) {
            throw new Error(e);
        }

        return connection;
        }
    }
}
